/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package uvexposureapplication.server;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.StringTokenizer;
import uvexposureapplication.eventrouting.Consumer;
import uvexposureapplication.eventrouting.Producer;
import uvexposureapplication.eventrouting.elvin.ElvinConsumer;
import uvexposureapplication.eventrouting.elvin.ElvinProducer;
import uvexposureapplication.eventrouting.elvin.notificationlistener.ServerNotificationListener;
import uvexposureapplication.rpc.AbstractPseudoRPCClientStub;
import uvexposureapplication.rpc.AbstractPseudoRPCServerStub;
import uvexposureapplication.rpc.clients.LocationServerPseudoRPCClientStub;
import uvexposureapplication.rpc.clients.PreferenceRepositoryPseudoRPCClientStub;
import uvexposureapplication.rpc.servers.ContextManagerPseudoRPCServerStub;
import uvexposureapplication.server.contextmanager.CityData;
import uvexposureapplication.server.preferencerepository.UserData;
import uvexposureapplication.utils.IOStream;

/**
 *
 * @author matthieukluj
 */
public class ContextManager implements Observer {

    public Consumer consumer;
    public Producer producer;
    public ServerNotificationListener ntf;
    public List<UserData> userDatas;
    public List<CityData> cityDatas;
    public List<String> indoorLocation;
    public AbstractPseudoRPCClientStub locationServerPseudoRPCClientStub;
    public AbstractPseudoRPCClientStub preferenceRepositoryPseudoRPCClientStub;
    public AbstractPseudoRPCServerStub contextManagerPseudoRPCServerStub;
    public AbstractPseudoRPCServerStub contextManagerPseudoRPCServerStub2;
    public AbstractPseudoRPCServerStub contextManagerPseudoRPCServerStub3;
    public String server;
    public Thread a, b, c, d, e;
    public int userNumbers;

    public ContextManager(String cityInfosFile, String server) {
        this.server = server;
        this.userNumbers = 0;
        this.ntf = new ServerNotificationListener();
        this.ntf.addObserver(this);

        this.indoorLocation = new LinkedList<String>();
        this.userDatas = new LinkedList<UserData>();
        this.cityDatas = new LinkedList<CityData>();

        this.consumer = new ElvinConsumer(server, this.ntf);
        this.producer = new ElvinProducer(server);

        this.consumer.subscribe("(Group == 'PreferenceManager') || (Group == 'LocationServer') || "
                + "(Group == 'Sensor' && (Type == 'Temperature' || "
                + "Type == 'UVR'))");

        this.locationServerPseudoRPCClientStub = new LocationServerPseudoRPCClientStub(this, "fetchIndoorLocation", null, server);
        a = new Thread(this.locationServerPseudoRPCClientStub);
        a.start();

        this.preferenceRepositoryPseudoRPCClientStub = new PreferenceRepositoryPseudoRPCClientStub(this, "fetchUserPreference", null, server);
        b = new Thread(this.preferenceRepositoryPseudoRPCClientStub);
        b.start();

        this.contextManagerPseudoRPCServerStub = new ContextManagerPseudoRPCServerStub(this, "searchItemsInLocation", server);
        c = new Thread(this.contextManagerPseudoRPCServerStub);
        c.start();

        this.contextManagerPseudoRPCServerStub2 = new ContextManagerPseudoRPCServerStub(this, "searchInfos", server);
        d = new Thread(this.contextManagerPseudoRPCServerStub2);
        d.start();

        this.contextManagerPseudoRPCServerStub2 = new ContextManagerPseudoRPCServerStub(this, "shutDown", server);
        e = new Thread(this.contextManagerPseudoRPCServerStub2);
        e.start();

        //this.userDatas = ;
        this.cityDatas = IOStream.readCityData(cityInfosFile);
    }

    public void setIndoorLocation(String indoorLocation) {
        StringTokenizer st = new StringTokenizer(indoorLocation, "-");
        while (st.hasMoreTokens()) {
            String location = st.nextToken();
            this.indoorLocation.add(location);
        }
        System.out.println("[RECEIVED] Indoor Location Set FROM LocationManager");
    }

    public String getItemsInLocation(String userName) {
        String items = "";
        String location = "";
        for (UserData userData : this.userDatas) {
            if (userData.getName().equals(userName)) {
                location = userData.getCurrentLocation();
            }
        }
        int counter = 0;
        for (CityData cData : this.cityDatas) {
            if (cData.getLocation().equals(location)) {
                counter = 1;
                items += "The following items of interest are in your location: \n\n";
                for (String service : cData.getServices()) {
                    items += service + "\n";
                }
            }
        }

        if (counter == 0) {
            items = "There are no items of interest in your current location.";
        }


        System.out.println("[SENDING] Items of Interests for a location TO UserInterface");
        return items;
    }

    public String getItemInformation(String itemName) {
        String infos = "";
        int found = 0;
        for (CityData cData : this.cityDatas) {
            if (cData.getName().equals(itemName)) {
                infos += "Information about " + cData.getName() + "\n\n";
                infos += cData.getInformation() + "\n";
                found = 1;
            } 
        }
        if (found == 0) {
            infos = "No Item Found";
        } else {
            infos.substring(0, infos.length() - 1);
        }
        System.out.println("[SENDING] Item Information TO UserInterface");
        return infos;
    }

    public Boolean overExposure(String timeElapsed, String userName, String uvr) {
        Boolean b = false;
        int skinType = 0;
        for (UserData userData : this.userDatas) {
            if (userData.getName().equals(userName)) {
                skinType = userData.getSkinType();
            }
        }
        int uvrI = Integer.parseInt(uvr);
        int timeElapsedI = Integer.parseInt(timeElapsed);

        // TODO Generalise that
        if (uvrI <= 2) {
            int timeThreshold = skinType * 15;
            if (timeElapsedI >= timeThreshold) {
                b = true;
            }
        } else if (uvrI <= 7) {
            int timeThreshold = skinType * 10;
            if (timeElapsedI >= timeThreshold) {
                b = true;
            }
        } else if (uvrI <= 11) {
            int timeThreshold = skinType * 5;
            if (timeElapsedI >= timeThreshold) {
                b = true;
            }
        }

        return b;
    }

    public Boolean tempMaxReached(String tempThreshold, String userName) {
        Boolean b = false;
        for (UserData userData : this.userDatas) {
            if (userData.getName().equals(userName)) {
                if (userData.getTempThreshold() <= Integer.parseInt(tempThreshold)) {
                    b = true;
                }
            }
        }
        return b;
    }

    public void setUserPreferences(String fileText) {
        StringTokenizer st = new StringTokenizer(fileText, "-");
        while (st.hasMoreTokens()) {
            String line = st.nextToken();
            StringTokenizer st2 = new StringTokenizer(line, ":");
            UserData userData = new UserData();
            while (st2.hasMoreTokens()) {
                String param = st2.nextToken();
                String value = st2.nextToken();
                if (param.equals("name")) {
                    userData.setName(value);
                } else if (param.equals("Skin Type")) {
                    userData.setSkinType(Integer.parseInt(value));
                } else if (param.equals("Temp")) {
                    userData.setTempThreshold(Integer.parseInt(value));
                }
            }
            this.userDatas.add(userData);
        }
        System.out.println("[RECEIVED] Initial User Preferences Set FROM PreferenceRepository");
    }

    public void produce(String parameter, String index) {

        //System.out.println("Received from Repository : " + parameter);
        String userName = "";
        String suggestion = "";
        String uv = "";
        String temp = "";

        System.out.println(parameter);
        StringTokenizer st = new StringTokenizer(parameter, ":");
        while (st.hasMoreTokens()) {
            userName = st.nextToken();
            suggestion = st.nextToken();
        }

        for (UserData usrData : this.userDatas) {
            if (usrData.getName().equals(userName)) {
                uv = Integer.toString(usrData.getUvrOld());
                temp = Integer.toString(usrData.getTempOld());
            }
        }

        Map<String, String> notification = new HashMap<String, String>();
        notification.put("Group", "ContextManager");
        notification.put("UserName", userName);
        notification.put("Index", index);
        notification.put("IndexUV", uv);
        notification.put("IndexTemp", temp);
        notification.put("Value", suggestion);
        this.producer.produceFromCollection(notification);
    }

    public void shutDown(String userName) {

        /*        synchronized (this.userDatas) {
        for (UserData usrData : this.userDatas) {
        if (usrData.getName().equals(userName)) {
        this.userDatas.remove(usrData);
        }
        }
        }
        
        
        if (this.userDatas.isEmpty()) {
         * */

        AbstractPseudoRPCClientStub prefManag = new PreferenceRepositoryPseudoRPCClientStub(this, "shutDown", userName, this.server);
        new Thread(prefManag).start();

        AbstractPseudoRPCClientStub locationServer = new LocationServerPseudoRPCClientStub(this, "shutDown", userName, this.server);
        new Thread(locationServer).start();

        this.close();
        //}
    }

    public void close() {
        this.consumer.closeConnection();
        this.producer.closeConnection();
        a.interrupt();
        b.interrupt();
        c.interrupt();
        d.interrupt();
        e.interrupt();
        System.exit(0);
    }

    @Override
    public void update(Observable o, Object arg) {
        if (arg instanceof Map) {
            Map<String, String> argMap = (Map<String, String>) arg;
            String userName = (String) argMap.get("UserName");
            UserData userData = new UserData();
            for (UserData usrData : this.userDatas) {
                if (usrData.getName().equals(userName)) {
                    userData = usrData;
                }
            }

            if (argMap.get("Group").equals("LocationServer")) {
                userData.setCurrentLocation((String) argMap.get("Coordinate"));
                System.out.println("[RECEIVED] currentPosition FROM LocationServer");
            }

            if (argMap.get("Group").equals("Sensor")) {
                if (argMap.get("Type").equals("UVR")) {
                    if ((userData.getUvrOld() == 0 || userData.getUvrOld() != Integer.parseInt((String) argMap.get("Value")))
                            && this.overExposure((String) argMap.get("Time"), userName, (String) argMap.get("Value"))) {

                        userData.setUvrOld(Integer.parseInt((String) argMap.get("Value")));
                        AbstractPseudoRPCClientStub prefManag = new PreferenceRepositoryPseudoRPCClientStub(this, "fetchUserPreferenceUV", userName, this.server);
                        new Thread(prefManag).start();
                        System.out.println("[RECEIVED] UVRSensor WARNING");

                        /*Map<String, String> notification = new HashMap<String, String>();
                        notification.put("Group", "ContextManager");
                        notification.put("Type", "UVRThreshold");
                        notification.put("UserName", userName);
                        this.producer.produceFromCollection(notification);
                         * * */
                    }
                }


                if (argMap.get("Type").equals("Temperature")) {
                    if ((userData.getTempOld() == 0 || userData.getTempOld() != Integer.parseInt((String) argMap.get("Value")))
                            && this.tempMaxReached((String) argMap.get("Value"), userName)) {

                        userData.setTempOld(Integer.parseInt((String) argMap.get("Value")));
                        AbstractPseudoRPCClientStub prefManag = new PreferenceRepositoryPseudoRPCClientStub(this, "fetchUserPreferenceTemp", userName, this.server);
                        new Thread(prefManag).start();
                        System.out.println("[RECEIVED] TempSensor WARNING");

                        /*
                        Map<String, String> notification = new HashMap<String, String>();
                        notification.put("Group", "ContextManager");
                        notification.put("Type", "TempThreshold");
                        notification.put("UserName", userName);
                        this.producer.produceFromCollection(notification);
                         * 
                         */
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        if (args.length < 2) {
            System.out.println("usage: java ContextManager "
                    + "[city-information-file][ElvinURL]");
            System.exit(0);
        }

        ContextManager cm = new ContextManager(args[0], args[1]);
    }
}
